package com.valotas.taglibs.scripting.groovy;


import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;

import groovy.lang.Binding;
import groovy.xml.MarkupBuilder;

/**
 * A Class inspired by {@link groovy.servlet.ServletBinding} that
 * handles the right binding of the dynamic attributes and some
 * other things that we may need at the tag implementation 
 * 
 * @author valotas@gmail.com
 * @version , 09 Jan 2009
 *
 */
public class TagBinding extends Binding {

	private final Binding binding;

	private final PageContext pageContext;

	private JspWriter out;
	
	private MarkupBuilder html;


	public TagBinding(final Map<String, Object> attributes, final PageContext context) {
		binding = new Binding();
		pageContext = context;

		binding.setVariable("pageContext", pageContext);
		binding.setVariable("context", pageContext.getServletContext());
		binding.setVariable("application", pageContext.getServletContext());
		binding.setVariable("attr", attributes);

		for (Entry<String, Object> attr : attributes.entrySet()) {
			String name = attr.getKey();
			if (!binding.getVariables().containsKey(name)) {
				binding.setVariable(name, attr.getValue());
			}
		}
	}


	@Override
	public void setVariable(String name, Object value) {
		/*
		 * Check sanity.
		 */
		if (name == null) {
			throw new IllegalArgumentException("Can't bind variable to null key.");
		}
		if (name.length() == 0) {
			throw new IllegalArgumentException("Can't bind variable to blank key name. [length=0]");
		}
		/*
		 * Check implicite key names. See getVariable(String)!
		 */
		if ("out".equals(name)) {
			throw new IllegalArgumentException("Can't bind variable to key named '" + name + "'.");
		}
		if ("html".equals(name)) {
			throw new IllegalArgumentException("Can't bind variable to key named '" + name + "'.");
		}

		/*
		 * All checks passed, set the variable.
		 */
		binding.setVariable(name, value);
	}


	/**
	 * @return a writer, an output stream, a markup builder or another requested object
	 */
	public Object getVariable(String name) {
		/*
		 * Check sanity.
		 */
		if (name == null) {
			throw new IllegalArgumentException("No variable with null key name.");
		}
		if (name.length() == 0) {
			throw new IllegalArgumentException("No variable with blank key name. [length=0]");
		}

		if ("request".equalsIgnoreCase(name)) {
			try {
				return (HttpServletRequest) pageContext.getRequest();
			}
			catch (ClassCastException e) {
				return pageContext.getRequest();
			}
		}
		
		if ("response".equalsIgnoreCase(name)) {
			try {
				return (HttpServletRequest) pageContext.getResponse();
			}
			catch (ClassCastException e) {
				return pageContext.getResponse();
			}
		}
		
		if ("out".equals(name)) {
			if (out == null) out = pageContext.getOut(); 
			return out;
		}
		
		if ("html".equals(name)) {
			if (html == null) {
				if (out == null) out = pageContext.getOut();
				html = new MarkupBuilder(out);
			}
			return html;
		}
		
		/*
		 * Still here? Delegate to the binding object.
		 */
		return binding.getVariable(name);
	}
	
	public JspWriter getOut() {
		return out;
	}
}
